use std::ops::Deref;

use crate::{modal::ModalCard, api};
use shared::{NetemProfile, LossConfig, DelayConfig, JitterConfig};
use wasm_bindgen_futures::spawn_local;
use ybc::{
    Button, Checkbox, Control, Field, Input,
};
use yew::prelude::*;

#[derive(Debug, Clone, Properties, PartialEq)]
pub struct Props {
    #[prop_or_default]
    pub profile: NetemProfile,
    #[prop_or_default]
    pub on_accepted: Callback<()>,
}

#[function_component]
pub fn AddProfileDialog(props: &Props) -> Html {
    let id = "add_profile";
    let title = "编辑模拟网络";
    let trigger = html! {
        <Button>{ "添加模拟网络" }</Button>
    };
    
    let name_state = use_state(|| props.profile.name.clone());
    let delay_state = use_state(|| props.profile.delay());
    let loss_state = use_state(|| props.profile.loss());
    let ppp_state = use_state(|| props.profile.is_poisson_loss());
    let jitter_state = use_state(|| props.profile.jitter());
    let burst_state = use_state(|| props.profile.burst().0);
    let gap_state = use_state(|| props.profile.burst().1);

    let on_save_btn_clicked = {
        let name_state = name_state.clone();
        let delay_state = delay_state.clone();
        let loss_state = loss_state.clone();
        let ppp_state = ppp_state.clone();
        let jitter_state = jitter_state.clone();
        let burst_state = burst_state.clone();
        let gap_state = gap_state.clone();
        let on_accepted = props.on_accepted.clone();
        let id = props.profile.id.clone();
        
        move |_| {
            let mut profile = NetemProfile::default();
            profile.id = id;
            profile.name = name_state.deref().clone();
            
            let delay = *delay_state;
            if delay > 0 {
                profile.cfgs.push(DelayConfig::Value(delay).into());
            }
            
            let loss = *loss_state;
            log::info!("final loss ratio {}", loss);
            if loss > 0.0 {
                if *ppp_state {
                    profile.cfgs.push(LossConfig::Poisson(loss).into());
                } else {
                    profile.cfgs.push(LossConfig::Stable(loss).into());
                }
            }
            
            let jitter = *jitter_state;
            if jitter > 0 {
                profile.cfgs.push(JitterConfig::Stable(jitter).into());
            }

            let burst = i16::min(*burst_state, 2000);
            let gap = i32::min(*gap_state, 60_000);
            if burst > 0 && gap > 0 {
                profile.cfgs.push(JitterConfig::Burst { burst, interval: gap }.into());
            }

            spawn_local(async move {
                let _ = api::create_profile(profile).await;
            });
            on_accepted.emit(());
        }
    };

    html! {
        <ModalCard {id} {title} {trigger} body={html! {
            <>
            <NameField name={name_state} />
            <DelayField delay={delay_state} />
            <LossField loss={loss_state} ppp={ppp_state} />
            <JitterField jitter={jitter_state} />
            <BurstField burst={burst_state} gap={gap_state} />
            </>
        }} footer={html! {
            <>
                <SaveProfileButton loading={false} onclick={on_save_btn_clicked} />
            </>
        }}/>
    }
}

#[derive(Properties, PartialEq)]
struct SaveProfileButtonProps {
    loading: bool,
    onclick: Callback<()>,
}

#[function_component]
fn SaveProfileButton(props: &SaveProfileButtonProps) -> Html {
    let is_active = use_context::<UseStateHandle<bool>>().expect("expect is_active context");
    let onclick = props.onclick.reform(move |_| {
        is_active.set(false);
    });

    html! {
        <Button classes={"is-success"} loading={props.loading} disabled={props.loading} {onclick}>{ "保存" }</Button>
    }
}

#[derive(Properties, PartialEq)]
struct NameFieldProps {
    name: UseStateHandle<String>,
}

#[function_component]
fn NameField(props: &NameFieldProps) -> Html {
    let name = use_state(|| props.name.deref().clone());
    let on_name_update = {
        let name = name.clone();
        let props_name = props.name.clone();
        move |value: String| {
            name.set(value.clone());
            props_name.set(value);
        }
    };

    html! {<>
        <Field classes={"is-horizontal"} label={"名称"} horizontal={true}>
            <Field>
                <Control>
                    <Input name={"delay"} value={ format!("{}", *name) } update={on_name_update}/>
                </Control>
            </Field>
        </Field>
    </>}
}

#[derive(Properties, PartialEq)]
struct DelayFieldProps {
    delay: UseStateHandle<i16>,
}

#[function_component]
fn DelayField(props: &DelayFieldProps) -> Html {
    let delay = use_state(|| *props.delay);
    let on_delay_update = {
        let delay = delay.clone();
        let props_delay = props.delay.clone();
        move |value: String| {
            let value = value.parse::<i16>().unwrap_or(*delay);
            delay.set(value);
            props_delay.set(value);
        }
    };

    html! {<>
        <Field classes={"is-horizontal"} label={"延迟(ms)"} horizontal={true}>
            <Field>
                <Control>
                    <Input name={"delay"} value={ format!("{}", *delay) } update={on_delay_update}/>
                </Control>
            </Field>
        </Field>
    </>}
}

#[derive(Properties, PartialEq)]
struct LossFieldProps {
    loss: UseStateHandle<f32>,
    ppp: UseStateHandle<bool>,
}

#[function_component]
fn LossField(props: &LossFieldProps) -> Html {
    let loss = use_state(|| *props.loss);
    let on_loss_update = {
        let loss = loss.clone();
        let props_loss = props.loss.clone();
        move |value: String| {
            let value = value.parse::<f32>().map(|f| f / 100.0).unwrap_or(*loss);
            log::info!("loss ratio changed to {}", value);
            loss.set(value);
            props_loss.set(value);
        }
    };

    let ppp = use_state(|| *props.ppp);
    let on_ppp_update = {
        let ppp = ppp.clone();
        let props_ppp = props.ppp.clone();
        move |checked: bool| {
            ppp.set(checked);
            props_ppp.set(checked);
        }
    };
    html! {<>
        <Field classes={"is-horizontal"} label={"丢包率(%)"} horizontal={true}>
            <Field>
                <Control>
                    <Input name={"loss"} value={ format!("{:.0}", *loss * 100.0) } update={on_loss_update}/>
                </Control>
            </Field>
        </Field>
        <Field classes={"is-horizontal"} label={""} horizontal={true}>
            <Field>
                <Control>
                    <Checkbox name={"ppp"} checked={*ppp} update={on_ppp_update}>{ "非均匀丢包" }</Checkbox>
                </Control>
            </Field>
        </Field>
    </>}
}

#[derive(Properties, PartialEq)]
struct JitterFieldProps {
    jitter: UseStateHandle<i16>,
}

#[function_component]
fn JitterField(props: &JitterFieldProps) -> Html {
    let jitter = use_state(|| *props.jitter);
    let on_jitter_update = {
        let jitter = jitter.clone();
        let props_jitter = props.jitter.clone();
        move |value: String| {
            let value = value.parse::<i16>().unwrap_or(*jitter);
            jitter.set(value);
            props_jitter.set(value);
        }
    };

    html! {<>
        <Field classes={"is-horizontal"} label={"抖动(ms)"} horizontal={true}>
            <Field>
                <Control>
                    <Input name={"jitter"} value={ format!("{}", *jitter) } update={on_jitter_update}/>
                </Control>
            </Field>
        </Field>
    </>}
}

#[derive(Properties, PartialEq)]
struct BurstFieldProps {
    burst: UseStateHandle<i16>,
    gap: UseStateHandle<i32>,
}

#[function_component]
fn BurstField(props: &BurstFieldProps) -> Html {
    let burst = use_state(|| *props.burst);
    let on_burst_update = {
        let burst = burst.clone();
        let props_burst = props.burst.clone();
        move |value: String| {
            let value = value.parse::<i16>().unwrap_or(*burst);
            burst.set(value);
            props_burst.set(value);
        }
    };

    let gap = use_state(|| *props.gap);
    let on_gap_update = {
        let gap = gap.clone();
        let props_gap = props.gap.clone();
        move |value: String| {
            let value = value.parse::<i32>().unwrap_or(*gap);
            gap.set(value);
            props_gap.set(value);
        }
    };
    html! {<>
        <Field classes={"is-horizontal"} label={"突发时长(ms)"} horizontal={true}>
            <Control>
                <Input name={"burst_length"} value={ burst.to_string() } update={on_burst_update}/>
            </Control>
        </Field>
        <Field classes={"is-horizontal"} label={"突发间隔(s)"} horizontal={true}>
            <Control>
                <Input name={"burst_gap"} value={ gap.to_string() } update={on_gap_update}/>
            </Control>
        </Field>
    </>}
}
